/*
 * OptionsForm.java
 *
 * Created on 15 juin 2006, 20:16
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package courselog;

import courselog.gpsutils.Converters;
import courselog.gpsutils.SerialGPSConnection;
import ewe.filechooser.FileChooser;
import ewe.fx.Color;
import ewe.fx.Dimension;
import ewe.fx.Font;
import ewe.io.SerialPortOptions;
import ewe.sys.Device;
import ewe.sys.VMApp;
import ewe.sys.Vm;
import ewe.sys.mThread;
import ewe.ui.CellConstants;
import ewe.ui.CellPanel;
import ewe.ui.CheckBoxGroup;
import ewe.ui.ControlConstants;
import ewe.ui.ControlEvent;
import ewe.ui.DataChangeEvent;
import ewe.ui.Editor;
import ewe.ui.Event;
import ewe.ui.Form;
import ewe.ui.Gui;
import ewe.ui.Menu;
import ewe.ui.MenuEvent;
import ewe.ui.MenuItem;
import ewe.ui.MessageBox;
import ewe.ui.Panel;
//import ewe.ui.mButton;
import ewe.ui.mCheckBox;
import ewe.ui.mInput;
import ewe.ui.mLabel;
import ewe.ui.mTextPad;

/**
 * Form to select the application options.
 * Appart from the logging file name which must be selected by the user,
 * this form allows to select to GPS port name and the minimum distance
 * to log displacements.
 * @author rigal
 */
public class OptionsForm extends CourseLogAppForm {
    
    /**
     * Vector of the proposed distances.
     */
    public final static double[] VDIST={5,10,20,50,100,2000};
    //public final static double[] VDIST={1,10,20,50,100,500}; // Debug with 1 m
    /** My calling application */
    MainApplication myApp;
    
    ActionKeySensitiveButton bGPSPort = new ActionKeySensitiveButton("Select GPS port");
    
    ActionKeySensitiveButton bCancel = new ActionKeySensitiveButton("Cancel");
    mLabel lGPSPortc=new mLabel(" COM6:");
    mCheckBox cD500=new mCheckBox("2 km");
    mCheckBox cD100=new mCheckBox("100 m");
    mCheckBox cD050=new mCheckBox(" 50 m");
    mCheckBox cD020=new mCheckBox(" 20 m");
    mCheckBox cD010=new mCheckBox(" 10 m");
    mCheckBox cD005=new mCheckBox("  5 m");
    CheckBoxGroup cbgDist=new CheckBoxGroup();
    
    /**
     * Vector of the proposed units.
     */
    public final static int[] VUNITS={Converters.FORMAT_KM_M,Converters.FORMAT_KT_NM};
    
    mCheckBox cKMHM=new mCheckBox(" km/h, m");
    mCheckBox cKTNM=new mCheckBox("  Kt, Nm");
    CheckBoxGroup cbgUnit=new CheckBoxGroup();
    
    /**
     * Input area for the logging file name.
     */
    protected mInput inpTrackFile = new mInput();
    
    /** Log file name as stored in that dialog. */
    public String myLogFileName;
    /**
     * Select logging file button.
     */
    protected ActionKeySensitiveButton bLog = new 
            ActionKeySensitiveButton(null, "ewe/opensmall.bmp", Color.White);
    
    /**
     * Input area for the Maps directory name.
     */
    protected mInput inpMapDir= new mInput();
    
    /** Maps directory name as stored in that dialog. */
    public String myMapDir;
    /**
     * Select Map dir button.
     */
    protected ActionKeySensitiveButton bMap= new 
            ActionKeySensitiveButton(null, "ewe/opensmall.bmp", Color.White);
    
    PullDownForKey bGoto;
    PullDownForKey bReset;
    
    // State fields
    /**
     * Minimum distance selected in the options form.
     */
    public double minDistance=50.0d;
    
    /** Units selected in the options form.
     */
    public int mySelectedUnits=Converters.FORMAT_KM_M;
    
    /**
     * Serial port options selected at the form level.
     */
    public SerialPortOptions dialogSPO=null;
    
    /**
     * Creates a new instance of OptionsForm.
     * Initialises the form controls and the current values.
     * @param app Application form to connect to this one.
     */
    public OptionsForm(MainApplication app) {
        myApp=app;
        
        setTitle("Options - CourseLog");
        Form.globalIcon = myApp.myAppGlobalIcon;
        
        Dimension dScr=Device.getScreenSize();
        
        MenuItem miSeparator=new MenuItem("-");
        /** menu for actions */
        MenuItem miCalibrate=new MenuItem("Calibrate$C","res/iconcalibsmall.png",Color.White);
        miCalibrate.action="C";
        MenuItem miNavigate=new MenuItem("Navigate$N","res/iconnavigate.png",Color.White);
        miNavigate.action="N";
        MenuItem miDistance=new MenuItem("Distance$D","res/courselogsmall.png",Color.White);
        miDistance.action="D";
        MenuItem miConvert=new MenuItem("Convert$v","res/convertsmall.png",Color.White);
        miConvert.action="V";
        MenuItem miCancel=new MenuItem("Cancel$a","ewe/deletesmall.bmp",Color.White);
        miCancel.action="A";
        MenuItem miReset=new MenuItem("Reset$R","ewe/newsmall.bmp",Color.White);
        miReset.action="R";
        MenuItem miExit=new MenuItem("Exit$x","ewe/exitsmall.bmp",Color.White);
        miExit.action="X";
        
        MenuItem[] tmiGoto=new MenuItem[]{miDistance,miNavigate,miCalibrate,
        miConvert,miSeparator,miExit};
        MenuItem[] tmiReset=new MenuItem[]{miReset,miCancel};
        Menu menuGotoActions=new Menu(tmiGoto,"GO TO");
        Menu menuResetActions=new Menu(tmiReset,"RESET");
        
        bGoto = new PullDownForKey("GO TO",menuGotoActions);
        ActionKeysDispatcher akdOpt=new ActionKeysDispatcher();
        bGoto.setActionKeyListener(akdOpt);
        
        bReset= new PullDownForKey("RESET",menuResetActions);
        bReset.setActionKeyListener(akdOpt);
        
        akdOpt.setLeftAndRight(bGoto,bReset);
        
        bCancel.setMyActioKeyDispatcher(akdOpt);
        bGPSPort.setMyActioKeyDispatcher(akdOpt);
        bLog.setMyActioKeyDispatcher(akdOpt);
        bMap.setMyActioKeyDispatcher(akdOpt);
        
        mTextPad lVersionMessage=new mTextPad(MainApplication.VERSIONMESSAGE);
        //lVersionMessage.set(mTextPad.DisplayOnly, true);
        lVersionMessage.set(mTextPad.Disabled,true);
        lVersionMessage.setCell(mTextPad.CENTER);
        CellPanel cPanelVersion = new CellPanel();
        cPanelVersion.setBorder(BDR_OUTLINE|BF_RECT,1);
        cPanelVersion.addLast(lVersionMessage);
        
        mLabel lGPSPortt=new mLabel("GPS port:");
        Font normalFont = lGPSPortt.getFont();
        
        int nHeight = normalFont.getSize();
        Font largeFont = normalFont.changeNameAndSize(null, nHeight*2);
        largeFont = largeFont.changeStyle(Font.BOLD);
        normalFont = normalFont.changeNameAndSize(null, nHeight+2);
        Font boldFont=normalFont.changeStyle(Font.BOLD);
        lGPSPortc.setFont(boldFont);
        
        cD005.setGroup(cbgDist);
        cD010.setGroup(cbgDist);
        cD020.setGroup(cbgDist);
        cD050.setGroup(cbgDist);
        cD100.setGroup(cbgDist);
        cD500.setGroup(cbgDist);
        
//        bGoto.setFont(largeFont);
//        bReset.setFont(largeFont);
        
        CellPanel cPanelOpt = new CellPanel();
        cPanelOpt.setBorder(BDR_OUTLINE|BF_RECT,1);
        cPanelOpt.setText("GPS connection");
        
        CellPanel cPanelDist = new CellPanel();
        cPanelDist.setBorder(BDR_OUTLINE|BF_RECT,1);
        cPanelDist.setText("Minimum Distance");
        cPanelDist.setToolTip("Set minimum distance between logged points.\n20m recommended");
        
        cPanelOpt.addNext(lGPSPortt,Form.DONTSTRETCH,Form.VCENTER);
        cPanelOpt.addLast(lGPSPortc,Form.FILL, Form.VCENTER);
        cPanelOpt.addLast(bGPSPort,Form.DONTSTRETCH, Form.VCENTER);
        
        cPanelDist.addNext(cD500,Form.FILL,Form.CENTER);
        cPanelDist.addNext(cD100,Form.FILL,Form.CENTER);
        cPanelDist.addLast(cD050,Form.FILL,Form.CENTER);
        cPanelDist.addNext(cD020,Form.FILL,Form.CENTER);
        cPanelDist.addNext(cD010,Form.FILL,Form.CENTER);
        cPanelDist.addLast(cD005,Form.FILL,Form.CENTER);
        
        
        CellPanel cPanelUnits = new CellPanel();
        cPanelUnits.setBorder(BDR_OUTLINE|BF_RECT,1);
        cPanelUnits.setText("Units");
        cPanelUnits.setToolTip("Set units to display.");
        cPanelUnits.addLast(cKMHM,Form.FILL,Form.CENTER);
        cPanelUnits.addLast(cKTNM,Form.FILL,Form.CENTER);
        cKMHM.setGroup(cbgUnit);
        cKTNM.setGroup(cbgUnit);
        
        // Initialise values from the application
        initFromApp();
        
        CellPanel cPanelLog=new CellPanel();
        mLabel lFile=new mLabel("Log:");
        cPanelLog.addNext(lFile);
        inpTrackFile.setText(myLogFileName);
//        inpTrackFile.addListener(this);
        cPanelLog.addNext(inpTrackFile,HGROW,FILL);
        cPanelLog.addLast(bLog);
        cPanelLog.setPreferredSize(dScr.width, dScr.height/16);
        
        CellPanel cPanelMap=new CellPanel();
        mLabel lMaps=new mLabel("Maps :");
        cPanelMap.addNext(lMaps);
        inpMapDir.setText(myMapDir);
        cPanelMap.addNext(inpMapDir);
        cPanelMap.addLast(bMap);
        cPanelMap.setPreferredSize(dScr.width, dScr.height/16);
        
        
        addLast(cPanelVersion,Form.STRETCH,Form.CENTER);
        addLast(cPanelOpt,Form.STRETCH,Form.CENTER);
        addNext(cPanelDist,Form.DONTCHANGE,Form.LEFT);
        addLast(cPanelUnits,Form.DONTCHANGE,Form.RIGHT);
        addLast(cPanelLog);
        addLast(cPanelMap);
        Vm.setSIP(Vm.SIP_LEAVE_BUTTON,this.getWindow());
        courseLogAppFormSetup(bGoto,bReset,null);
    }
    
    // CLASS METHODS
    
    /**
     * Dispatch the action from a form button.
     * @param actString String associated to the pressed button to determine the action.
     */
    public void executeAction(String actString) {
        if(actString=="X") tryExitForm(-1);
        else if(actString=="C") tryExitForm(MainApplication.ACTIVECALIBRATION);
        else if(actString=="V") tryExitForm(MainApplication.ACTIVECONVERT);
        else if(actString=="N") tryExitForm(MainApplication.ACTIVENAVIGATE);
        else if(actString=="D") tryExitForm(MainApplication.ACTIVEDISTANCE);
        else if(actString=="R") resetFromApp();
        else if(actString=="A") initFromApp();
    }
    
    /**
     * Triggers an exit from the application
     * @param nextState Index of the next state of the application. <I>-1</I> means exit.
     */
    public void tryExitForm(int nextState) {
        updateValues();
        myApp.myNextActivePanel=nextState; // Specify next to the application
        exit(Form.IDOK);
    }
    
    /**
     * Updates the application's settings from the current options values.
     */
    public void updateValues() {
        myLogFileName=this.inpTrackFile.getText();
        myMapDir=this.inpMapDir.getText();
        if(myApp.writeOptionsFile()) return;
        MessageBox mbErr=new MessageBox("Error","Error writing options file :"+
                myApp.OPTIONSFILENAME,MessageBox.MBOK);
        mbErr.execute();
        return;
    }
    
    /**
     * (Re-)Initialises the form from the application's values, discarding the
     * current values.
     */
    public void initFromApp() {
        this.dialogSPO=myApp.mySPO;
        this.minDistance=myApp.myMinDistance;
        setCheckFromDistance(this.minDistance);
        mySelectedUnits=Converters.classCurrentFormat;
        myLogFileName=myApp.myLogFileName;
        myMapDir=myApp.myMapDir;
        this.inpTrackFile.setText(myLogFileName);
        this.inpMapDir.setText(myMapDir);
        setCheckFromUnits(mySelectedUnits);
        updateSerialOptions();
//        repaint();
    }
    
    /**
     * Initialises the form from the application's default values, discarding the
     * current values.
     */
    public void resetFromApp() {
        this.dialogSPO=SerialGPSConnection.getDefaultPortOptions();
        this.minDistance=MainApplication.getDefaulMinDistance();
        setCheckFromDistance(this.minDistance);
        this.mySelectedUnits=MainApplication.getDefaulUnits();
        setCheckFromUnits(this.mySelectedUnits);
        this.myLogFileName=MainApplication.getDefaulLogFilePath();
        this.myMapDir=MainApplication.getDefaultMapDirPath();
        this.inpTrackFile.setText(myLogFileName);
        this.inpMapDir.setText(myMapDir);
        updateSerialOptions();
//        repaint();
    }
    
    /**
     * Check the corresponding controls according to a given distance value.
     * @param dist Minimum distance value, set to the accepted checkable values.
     */
    public void setCheckFromDistance(double dist) {
        if(dist>=VDIST[5]) {
            cbgDist.selectIndex(5);
        }else if(dist>=VDIST[4]) {
            cbgDist.selectIndex(4);
        }else if(dist>=VDIST[3]) {
            cbgDist.selectIndex(3);
        }else if(dist>=VDIST[2]) {
            cbgDist.selectIndex(2);
        }else if(dist>=VDIST[1]) {
            cbgDist.selectIndex(1);
        }else{
            cbgDist.selectIndex(0);
        }
    }
    
    /**
     * Provides the minimum distance value according to the checked
     * item.
     * @return Checked distance.
     * @see #VDIST
     */
    public double getDistanceFromCheck() {
        return OptionsForm.VDIST[cbgDist.getSelectedIndex()];
    }
    
    /**
     * Check the corresponding controls according to a given units set.
     * @param val values as from Converters class.
     */
    public void setCheckFromUnits(int val) {
        for(int idx=VUNITS.length-1; idx>=0; idx--) {
            if(val==VUNITS[idx]) {
                cbgUnit.selectIndex(idx);
                return;
            }
        }
        cbgUnit.selectIndex(0);
        return;
    }
    
    /**
     * Get the selected units set.
     * @return Value corresponding to Converters.FORMATxxx values.
     * @see courselog.gpsutils.Converters
     */
    public int getUnitFromCheck() {
        return VUNITS[cbgUnit.getSelectedIndex()];
    }
    
    /**
     * Sets the port name label according to the current value of dialog SPO.
     */
    public void updateSerialOptions() {
        lGPSPortc.setText(this.dialogSPO.portName);
    }
    
    /**
     * Serial port dialog button was pressed, open the advanced dialog.
     */
    public void openComPortOptions() {
        Editor s = dialogSPO.getEditor(dialogSPO.ADVANCED_EDITOR);
        Gui.setOKCancel(s);
        if (s.execute() == IDCANCEL) return;
        updateSerialOptions();
    }
    
    /**
     * Called when the user whats to select the track logging file.
     * Opens a FileDialog at the location of the last track file.
     * Track files are not overwritten, new trace points are appended at
     * the end of the file.
     */
    public void selectTrackFile() {
//        logger.addToLog("\n Select track file pressed");
        FileChooser fc=new FileChooser(FileChooser.SAVE, myLogFileName);
        if(fc.execute()==fc.IDCANCEL) return;
        myLogFileName=fc.getChosen();
        inpTrackFile.setText(myLogFileName);
        if(myLogFileName=="") myLogFileName=null;
    }
    
    /**
     * Called when the user whats to select the Maps directory.
     * Opens a FileDialog at the location of the last Maps dir.
     */
    public void selectMapsDir() {
        FileChooser fc=new FileChooser(FileChooser.DIRECTORY_SELECT, myMapDir);
        if(fc.execute()==fc.IDCANCEL) return;
        myMapDir=fc.getChosen();
        inpMapDir.setText(myMapDir);
    }
    
    // OVERIDDEN METHODS
    
    /**
     * When a control is activated, reacts to control press.
     * @param ev Control event.
     */
    public void onEvent(Event ev) {
        try{
            if((ev.target==bGoto)||(ev.target==bReset)) {
                if(ev.type==MenuEvent.SELECTED) {
//                CourseLogAppForm.dbgL.addToLog("\n pressed:"+ev.target+" item "+pdFileActions.getLastSelected());
                    MenuEvent mEv=(MenuEvent) ev;
                    final MenuItem selItem= (MenuItem) mEv.selectedItem;
//                CourseLogAppForm.dbgL.addToLog("\n selected:"+selItem.action);
                    mThread actThread= new mThread(new Runnable() {
                        public void run() {
                            executeAction(selItem.action);
                            //updateFormState();
                        }
                    });
                    actThread.start();
                }
            }else if((ev.target==bGPSPort)&&(ev.type==ControlEvent.PRESSED)) {
                openComPortOptions();
                return;
            }else if((ev.target==bLog)&&(ev.type==ControlEvent.PRESSED)) {
                selectTrackFile();
                return;
//        }else if((ev.target==inpTrackFile)&&(ev instanceof DataChangeEvent)) {
//            myLogFileName=inpTrackFile.getText();
//            return;
            }else if((ev.target==bMap)&&(ev.type==ControlEvent.PRESSED)) {
                selectMapsDir();
                return;
            }else if((ev.target==cbgDist)&&(ev.type==ControlEvent.PRESSED)) {
                this.minDistance=getDistanceFromCheck();
            }else if((ev.target==cbgUnit)&&(ev.type==ControlEvent.PRESSED)) {
                this.mySelectedUnits=getUnitFromCheck();
            }else if((ev.target==this)&&(ev.type==ControlEvent.FOCUS_IN)) {
                myApp.myNextActivePanel=-1;
            }else {
                super.onEvent(ev);
            }
        } catch (NullPointerException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
        } catch (IndexOutOfBoundsException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
        } catch (ClassCastException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
        }
    }
}
